/*
 * xmmap.h
 *
 *  Created on: Aug 18, 2011
 *      Author: lindenb
 */

#ifndef XMMAP_H_
#define XMMAP_H_
#include <cerrno>
#include <iostream>
#include <stdexcept>

#include "xstring.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "xstdio.h"

template<typename T>
class MappedFile
    {
    protected:
    	/* number of items in the file */
	std::size_t _nitems;
	MappedFile():_nitems(0)
	    {
	    }
    public:
	typedef std::size_t size_type;
	virtual ~MappedFile()
	    {
	    }
	/* @return number of items in the file */
	virtual size_type size() const
	    {
	    return _nitems;
	    }
	/* copy idx-th item in object. return 0 on success */
	virtual int at(size_type idx,T* object) const=0;
	/* @return idx-th item */
	virtual T at(size_type idx) const=0;
    };

template<typename T>
class MMappedFile:public MappedFile<T>
    {
    private:
	/* used to get the size of the file */
	struct stat buf;
	/* genome fasta file file descriptor */
	int fd;
	/* the mmap (memory mapped) pointer */
	T *mapptr;
    public:
	typedef typename MappedFile<T>::size_type size_type;

	MMappedFile(const char* filename):MappedFile<T>(),fd(-1),mapptr(NULL)
	    {
	    /* get the whole size of file */
	    if(stat(filename, &buf)!=0)
		{
		std::cerr << "Cannot stat " << filename << " "<< strerror(errno) << std::endl;
		throw std::runtime_error("I/O error");
		}
	    this->_nitems=buf.st_size/sizeof(T);

	    /* open the fasta file */
	    fd = open(filename, O_RDONLY);
	    if (fd == -1)
		{
		perror("Error opening file for reading");
		throw std::runtime_error("I/O error");
		}
	    /* open a memory mapped file associated to this fasta file descriptor */
	    mapptr = (T*)mmap(0, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
	    if (mapptr == MAP_FAILED)
		{
		close(fd);
		perror("Error mmapping the file");
		throw std::runtime_error("I/O error");
		}
	    }

	virtual ~MMappedFile()
	    {
	    /* close memory mapped map */
	    if(mapptr!=NULL && munmap(mapptr,buf.st_size) == -1)
		    {
		    perror("Error un-mmapping the file");
		    }
	     /* dispose fasta file descriptor */
	    if(fd!=-1) close(fd);
	    }

	virtual int at(size_type idx,T* object) const
	    {
	    std::memcpy(object,&mapptr[idx],sizeof(T));
	    return 0;
	    }

	virtual T at(size_type idx) const
	    {
	    return  mapptr[idx];
	    }
    };

template<typename T>
class StdioMappedFile:public MappedFile<T>
    {
    private:
	/* file descriptor */
	std::FILE* fd;
	/* current pos */
	mutable long fpos;
    public:
	typedef typename MappedFile<T>::size_type size_type;
	StdioMappedFile(const char* filename):fd(NULL),fpos(0)
	    {
	    fd=safeFOpen(filename,"r");
	    safeFSeek(fd,0L,SEEK_END);
	    this->_nitems = std::ftell(fd)/sizeof(T);
	    safeFSeek(fd,0L,SEEK_SET);
	    }
	virtual ~StdioMappedFile()
	    {
	    if(fd!=NULL)  std::fclose(fd);
	    }
	virtual int at(size_type index,T* object) const
	    {
	    if(this->fpos!=(long int)index)
		{
		safeFSeek(this->fd,index*sizeof(T),SEEK_SET);
		}
	    safeFRead(object,sizeof(T),1,fd);
	    this->fpos=index+1;
	    return 0;
	    }

	virtual T at(size_type idx) const
	    {
	    T c;
	    at(idx,&c);
	    return c;
	    }
    };

#endif /* XMMAP_H_ */
